﻿//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: SqlCommandBuilderBase.cs
//
//  Description: Base class for object expression to SQL command builder.
// 
//--------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Text;
using Chili.Opf3.Query;
using Chili.Opf3.Query.OPathParser;
using Chili.Opf3.Mapping;

namespace Chili.Opf3.Storages
{
	/// <summary>
	/// Abstract class that converts <see cref="Opf3.Query.ObjectExpression">ObjectExpression</see> 
	/// classes to storage dependent <see cref="Opf3.Query.CompiledQuery">CompiledQuery</see> classes (using SQL).
	/// </summary>
	/// <remarks>
	/// It is used by each storage to create SQL command and is not indented to be used in the user code.
	/// </remarks>
	public abstract class SqlCommandBuilderBase : IStorageCommandBuilder
	{
		private bool _insertAutoNumber;

		/// <summary>
		/// Creates a new instance of the <see cref="SqlCommandBuilderBase">SqlCommandBuilderBase Class</see>.
		/// </summary>
		/// <param name="insertAutoNumber">True if properties with AutoNumbers (see <see cref="Opf3.FieldAttribute">FieldAttribute</see>) are inserted in the query.</param>
		/// <exception cref="ArgumentNullException">ProviderFactory is null.</exception>
		protected SqlCommandBuilderBase(bool insertAutoNumber)
		{
			_insertAutoNumber = insertAutoNumber;
		}

		#region BuildSelectQuery

		/// <summary>
		/// Creates the <see cref="Opf3.Query.CompiledQuery">CompildQuery</see> that contains a storage dependent 
		/// select query by using the <see cref="Opf3.Query.ObjectExpression">ObjectExpression</see> given.
		/// </summary>
		/// <param name="objectExpression">
		/// 	<see cref="Opf3.Query.ObjectExpression">ObjectExpression</see> that is converted
		/// to a <see cref="Opf3.Query.CompiledQuery">CompildQuery</see>.
		/// </param>
		/// <param name="sqlStorageBase">The storage that the query is compiled for.</param>
		/// <exception cref="ArgumentNullException">ObjectExpression is null.</exception>
		protected virtual CompiledQuery BuildSelectQuery(SqlStorageBase sqlStorageBase, ObjectExpression objectExpression)
		{
			if (objectExpression == null)
				throw new ArgumentNullException("objectExpression");

			// Create the string builder that holds the final query.
			StringBuilder query = null;
			// Loop over all persistent attributes.
			for (int i = 0; i < objectExpression.PersistentAttributes.Count; i++)
			{
				// Get the entity.
				string entity = objectExpression.PersistentAttributes[i].Entity;

				if (query == null)
					query = new StringBuilder(string.Format("select {0}.* from {0}", sqlStorageBase.GetValidEntityName(entity)));
				else
					query.Append(string.Format(", {0}", sqlStorageBase.GetValidEntityName(entity)));
			}

			StringBuilder sortBy = new StringBuilder();
			// Create the command.
			IDbCommand command = sqlStorageBase.CreateCommand();
			int paramIndex = 0;

			if (objectExpression.Items.Count > 0)
			{
				foreach (OEItem item in objectExpression.Items)
				{
					if (item is OESortBy)
					{
						// Add the sort by, which becomes "order by" in sql.
						sortBy.Append(" order by");

						IList<OEItem> sortByMembers = item.Items;
						for (int i = 0; i < sortByMembers.Count; i++)
						{
							if (i > 0)
								sortBy.Append(',');

							CreateSelectQuery(sqlStorageBase, sortByMembers[i], sortBy, command, ref paramIndex);
							sortBy.Append(((OESortByMember)sortByMembers[i]).SortOrder == SortOrders.Ascending ? " Asc" : " Desc");
						}
					}
					else if (item is OEWhereCondition)
					{
						if (item.Items.Count > 0)
						{
							// Add the where clause.
							query.Append(" where");
							// Create the select query.
							CreateSelectQuery(sqlStorageBase, item, query, command, ref paramIndex);
						}
					}
				}
			}

			command.CommandText = query.ToString() + sortBy.ToString();
			CompiledQuery compiledQuery = new CompiledQuery(command, QueryTypes.Load);
			// Set the timeout for the query.
			compiledQuery.Timeout = objectExpression.Timeout;
			return compiledQuery;
		}

		/// <summary>
		/// Creates one piece of the select query. The item that is given is processed.
		/// </summary>
		/// <param name="sqlStorageBase">The storage that the query is compiled for.</param>
		/// <param name="item">Item that is processed by this method.</param>
		/// <param name="query">The query that has been compiled until the call of this method.</param>
		/// <param name="command">The command object that is then embedded into the compiled query and
		/// executed on the storage.</param>
		/// <param name="paramIndex">The index of the current parameter that is inserted into the query.</param>
		private void CreateSelectQuery(SqlStorageBase sqlStorageBase, OEItem item, StringBuilder query, IDbCommand command, ref int paramIndex)
		{
			foreach (OEItem innerItem in item.Items)
			{
				if (innerItem is OEBrackets)
				{
					// Insert the brackets in the select query.
					query.Append(" (");
					CreateSelectQuery(sqlStorageBase, innerItem, query, command, ref paramIndex);
					query.Append(" )");
				}
				else if (innerItem is OEComparer)
					query.Append(" " + ((OEComparer)innerItem).Value);
				else if (innerItem is OEConcat)
					query.Append(" " + ((OEConcat)innerItem).Value);
				else if (innerItem is OECondition)
				{
					// Call the method again since a condition contains other items.
					CreateSelectQuery(sqlStorageBase, innerItem, query, command, ref paramIndex);
				}
				else if (innerItem is OEInlineParameter)
					query.Append(" " + ((OEInlineParameter)innerItem).Value);
				else if (innerItem is OEJoin)
				{
					query.Append(string.Format(" exists (select {0}.* from {0} where",
						sqlStorageBase.GetValidEntityName(((OEJoin)innerItem).PersistentAttribute.Entity)));
					CreateSelectQuery(sqlStorageBase, innerItem, query, command, ref paramIndex);
					query.Append(")");
				}
				else if (innerItem is OEParameter)
				{
					OEParameter oeParameter = (OEParameter)innerItem;
					string parameterName = null;
					IDataParameter parameter = CreateParameter(sqlStorageBase, paramIndex, oeParameter.Value,
						oeParameter.ValueType, out parameterName);
					// Increment the parameter index.
					paramIndex++;
					// Add the parameter name, that has been created by the method.
					query.Append(" " + parameterName);
					// Add the parameter to the command object.
					command.Parameters.Add(parameter);
				}
				else if (innerItem is OEMember)
				{
					// Add the property to the query.
					OEMember member = ((OEMember)innerItem);

					string fieldName = string.Format("{0}.{1}", sqlStorageBase.GetValidEntityName(member.PersistentAttribute.Entity),
						sqlStorageBase.GetValidFieldName(member.FieldAttribute.FieldName));
					query.Append(" " + fieldName);
				}
				else if (innerItem is OECommand)
				{
					// Try to add a command. The command is added by the storage.
					string storageCommand = ConvertCommand((OECommand)innerItem);
					int bracket = storageCommand.IndexOf("{0}");

					// Add the first part of the command, call then the method again and after that
					// add the second part of the command.
					query.Append(" ");
					query.Append(storageCommand.Substring(0, bracket));
					CreateSelectQuery(sqlStorageBase, innerItem, query, command, ref paramIndex);
					query.Append(storageCommand.Substring(bracket + 3, storageCommand.Length - bracket - 3));
					query.Append(" ");
				}
			}
		}

		#endregion

		#region BuildInsertQuery

		/// <summary>
		/// Creates the <see cref="Opf3.Query.CompiledQuery">CompildQuery</see> that contains a storage dependent 
		/// update query by using the <see cref="Opf3.Query.PersistentObjectExpression">PersistentObjectExpression</see> given.
		/// </summary>
		/// <param name="poe">
		/// 	<see cref="Opf3.Query.PersistentObjectExpression">PersistentObjectExpression</see> that is converted
		/// to a <see cref="Opf3.Query.CompiledQuery">CompildQuery</see>.
		/// </param>
		/// <param name="sqlStorageBase">The storage that the query is compiled for.</param>
		/// <exception cref="ArgumentNullException">PersistentObjectExpression is null.</exception>
		/// <exception cref="ArgumentNullException">SqlStorageBase is null.</exception>
		protected virtual CompiledQuery BuildInsertQuery(SqlStorageBase sqlStorageBase, PersistentObjectExpression poe)
		{
			if (sqlStorageBase == null)
				throw new ArgumentNullException("sqlStorageBase");
			if (poe == null)
				throw new ArgumentNullException("poe");
			// Check if the storage implements the ICustomDataTypesStorage interface.
			ICustomDataTypesStorage customDataTypesStorage = sqlStorageBase as ICustomDataTypesStorage;

			// Create the command.
			IDbCommand command = sqlStorageBase.CreateCommand();
			CompiledQuery compiledQuery = new CompiledQuery(command, QueryTypes.Insert);
			// Set the timeout for the query.
			compiledQuery.Timeout = poe.Timeout;

			// Create the string builder for the query.
			StringBuilder fields = new StringBuilder();
			StringBuilder parameters = new StringBuilder();

			// Get the persistent, the mapping and the list of members.
			object persistent = poe.Persistent;
			IPartialPersist partial = persistent as IPartialPersist;
			TypeMapping mapping = TypeMapping.GetTypeMapping(persistent);
			IMemberInfoCollection members = mapping.Members;

			for (int i = 0; i < members.Count; i++)
			{
				IMemberInfo mi = members[i];
				FieldAttribute fa = mi.FieldAttribute;

				// Skip if the member should not be inserted.
				if ((fa.PersistBehaviours & PersistBehaviours.DontInsert) != 0)
					continue;
				// Skip members are not to persist.
				if (partial != null && !partial.IsMemberToPersist(mi.Name, PersistentOperations.Insert))
					continue;

				// Check if the condition is an autonumber. This must be always performed also if the
				// value of the parameter is null. It is never skipped.
				if (fa.AutoNumber)
				{
					// Set the persistent attribute for the autonumbers.
					if (compiledQuery.AutoNumberItems == null)
						compiledQuery.AutoNumberItems = new AutoNumberItems(mapping.PersistentAttribute);

					// Add the field attribute to the fields.
					compiledQuery.AutoNumberItems.Add(new AutoNumberItem(fa));

					// Loop over autonumber if set so.
					if (!_insertAutoNumber)
						continue;
				}

				object value = mi.GetValue(persistent);
				// Convert the values back to the storage compatible values.
				if (customDataTypesStorage != null)
					value = customDataTypesStorage.ChangeToDataType(value);
				// Ignore null values. They are not used in the insert.
//				if (value == null)
//					continue;

				// Create the parameter.
				string parameterName = null;
				IDataParameter dataParameter = CreateParameter(sqlStorageBase, i, value, mi.MemberType, out parameterName);

				if (fields.Length == 0)
				{
					fields.Append(sqlStorageBase.GetValidFieldName(fa.FieldName));
					parameters.Append(parameterName);
				}
				else
				{
					fields.AppendFormat(", {0}", sqlStorageBase.GetValidFieldName(fa.FieldName));
					parameters.AppendFormat(", {0}", parameterName);
				}

				// Add the parameter to the command.
				command.Parameters.Add(dataParameter);
			}

			// Create the string builder that holds the final query.
			command.CommandText = string.Format("insert into {0} ({1}) values ({2})", sqlStorageBase.GetValidEntityName(mapping.PersistentAttribute.Entity), fields, parameters);
			return compiledQuery;
		}

		/// <summary>
		/// Creates the <see cref="Opf3.Query.CompiledQuery">CompildQuery</see> that contains a storage dependent 
		/// insert query by using the <see cref="Opf3.Query.ObjectExpression">ObjectExpression</see> given.
		/// </summary>
		/// <param name="objectExpression">
		/// 	<see cref="Opf3.Query.ObjectExpression">ObjectExpression</see> that is converted
		/// to a <see cref="Opf3.Query.CompiledQuery">CompildQuery</see>.
		/// </param>
		/// <param name="sqlStorageBase">The storage that the query is compiled for.</param>
		/// <exception cref="ArgumentNullException">ObjectExpression is null.</exception>
		/// <exception cref="ArgumentNullException">SqlStorageBase is null.</exception>
		protected virtual CompiledQuery BuildInsertQuery(SqlStorageBase sqlStorageBase, ObjectExpression objectExpression)
		{
			if (sqlStorageBase == null)
				throw new ArgumentNullException("sqlStorageBase");
			if (objectExpression == null)
				throw new ArgumentNullException("objectExpression");

			// Create the command.
			IDbCommand command = sqlStorageBase.CreateCommand();
			CompiledQuery compiledQuery = new CompiledQuery(command, QueryTypes.Insert);
			// Set the timeout for the query.
			compiledQuery.Timeout = objectExpression.Timeout;

			StringBuilder fields = new StringBuilder();
			StringBuilder parameters = new StringBuilder();
			int paramIndex = 0;

			foreach (OECondition condition in objectExpression.Items)
			{
				// Get the items in the condition.
				OEMember member = condition.Items[0] as OEMember;
				OEParameter parameter = (OEParameter)condition.Items[1];

				// Check if the condition is an autonumber. This must be always performed also if the
				// value of the parameter is null. It is never skipped.
				if (member != null && member.FieldAttribute.AutoNumber)
				{
					// Set the persistent attribute for the autonumbers.
					if (compiledQuery.AutoNumberItems == null)
						compiledQuery.AutoNumberItems = new AutoNumberItems(objectExpression.PersistentAttributes[0]);

					// Add the field attribute to the fields.
					compiledQuery.AutoNumberItems.Add(new AutoNumberItem(member.FieldAttribute));

					// Loop over autonumber if set so.
					if (!_insertAutoNumber)
						continue;
				}

				// Ignore null values. They are not used in the insert.
//				if (parameter.Value == null)
//					continue;

				string parameterName = null;
				IDataParameter dataParameter = CreateParameter(sqlStorageBase, paramIndex, parameter.Value, parameter.ValueType, out parameterName);
				// Increment the parameter index.
				paramIndex++;

				// Check if we are dealing with a property or with a field.
				string fieldName = null;
				if (member != null)
					fieldName = member.FieldAttribute.FieldName;
				else
					fieldName = ((OEField)condition.Items[0]).Name;

				if (fields.Length == 0)
				{
					fields.Append(sqlStorageBase.GetValidFieldName(fieldName));
					parameters.Append(parameterName);
				}
				else
				{
					fields.Append(string.Format(", {0}", sqlStorageBase.GetValidFieldName(fieldName)));
					parameters.Append(string.Format(", {0}", parameterName));
				}

				// Add the parameter to the command.
				command.Parameters.Add(dataParameter);
			}

			// Get the entity.
			string entity = objectExpression.PersistentAttributes[0].Entity;
			// Create the string builder that holds the final query.
			command.CommandText = string.Format("insert into {0} ({1}) values ({2})", sqlStorageBase.GetValidEntityName(entity),
				fields, parameters);

			return compiledQuery;
		}

		#endregion

		#region BuildDeleteQuery

		/// <summary>
		/// Creates the <see cref="Opf3.Query.CompiledQuery">CompildQuery</see> that contains a storage dependent 
		/// update query by using the <see cref="Opf3.Query.PersistentObjectExpression">PersistentObjectExpression</see> given.
		/// </summary>
		/// <param name="poe">
		/// 	<see cref="Opf3.Query.PersistentObjectExpression">PersistentObjectExpression</see> that is converted
		/// to a <see cref="Opf3.Query.CompiledQuery">CompildQuery</see>.
		/// </param>
		/// <param name="sqlStorageBase">The storage that the query is compiled for.</param>
		/// <exception cref="ArgumentNullException">PersistentObjectExpression is null.</exception>
		/// <exception cref="ArgumentNullException">SqlStorageBase is null.</exception>
		protected virtual CompiledQuery BuildDeleteQuery(SqlStorageBase sqlStorageBase, PersistentObjectExpression poe)
		{
			if (sqlStorageBase == null)
				throw new ArgumentNullException("sqlStorageBase");
			if (poe == null)
				throw new ArgumentNullException("poe");
			// Check if the storage implements the ICustomDataTypesStorage interface.
			ICustomDataTypesStorage customDataTypesStorage = sqlStorageBase as ICustomDataTypesStorage;

			IDbCommand command = sqlStorageBase.CreateCommand();
			StringBuilder query = new StringBuilder();

			// Get the persistent, the mapping and the list of members.
			object persistent = poe.Persistent;
			TypeMapping mapping = TypeMapping.GetTypeMapping(persistent);
			IIdentifierMemberInfoCollection members = mapping.Members.GetIdentifierMemberMappings();

			// Initialize the query.
			query.Append(string.Format("delete from {0}", sqlStorageBase.GetValidEntityName(mapping.PersistentAttribute.Entity)));

			for (int i = 0; i < members.Count; i++)
			{
				IMemberInfo mi = members[i];
				// Create the parameter.
				string parameterName = null;

				object value = mi.GetValue(persistent);
				// Convert the values back to the storage compatible values.
				if (customDataTypesStorage != null)
					value = customDataTypesStorage.ChangeToDataType(value);

				IDataParameter dataParameter = CreateParameter(sqlStorageBase, i, value, mi.MemberType, out parameterName);

				query.AppendFormat((i == 0 ? " where {0} = {1}" : " and {0} = {1}"), sqlStorageBase.GetValidFieldName(mi.FieldAttribute.FieldName), parameterName);

				// Add the parameter to the command.
				command.Parameters.Add(dataParameter);
			}
			command.CommandText = query.ToString();
			CompiledQuery compiledQuery = new CompiledQuery(command, QueryTypes.Delete);
			// Set the timeout for the query.
			compiledQuery.Timeout = poe.Timeout;
			return compiledQuery;
		}

		/// <summary>
		/// Creates the <see cref="Opf3.Query.CompiledQuery">CompildQuery</see> that contains a storage dependent 
		/// delete query by using the <see cref="Opf3.Query.ObjectExpression">ObjectExpression</see> given.
		/// </summary>
		/// <param name="objectExpression">
		/// 	<see cref="Opf3.Query.ObjectExpression">ObjectExpression</see> that is converted
		/// to a <see cref="Opf3.Query.CompiledQuery">CompildQuery</see>.
		/// </param>
		/// <param name="sqlStorageBase">The storage that the query is compiled for.</param>
		/// <exception cref="ArgumentNullException">SqlStorageBase is null.</exception>
		/// <exception cref="ArgumentNullException">ObjectExpression is null.</exception>
		protected virtual CompiledQuery BuildDeleteQuery(SqlStorageBase sqlStorageBase, ObjectExpression objectExpression)
		{
			if (sqlStorageBase == null)
				throw new ArgumentNullException("sqlStorageBase");
			if (objectExpression == null)
				throw new ArgumentNullException("objectExpression");

			IDbCommand command = sqlStorageBase.CreateCommand();
			StringBuilder query = new StringBuilder();
			int paramIndex = 0;
			// Get the entity.
			string entity = objectExpression.PersistentAttributes[0].Entity;

			// Initialize the query.
			query.Append(string.Format("delete from {0}", sqlStorageBase.GetValidEntityName(entity)));

			foreach (OECondition condition in objectExpression.Items[0].Items)
			{
				// Get the items in the condition.
				OEMember member = (OEMember)condition.Items[0];
				OEParameter parameter = (OEParameter)condition.Items[1];

				string parameterName = null;
				IDataParameter dataParameter = CreateParameter(sqlStorageBase, paramIndex, parameter.Value,
					parameter.ValueType, out parameterName);
				// Increment the parameter index.
				paramIndex++;

				if (paramIndex == 1)
				{
					query.Append(string.Format(" where {0} = {1}",
						sqlStorageBase.GetValidFieldName(member.FieldAttribute.FieldName), parameterName));
				}
				else
				{
					query.Append(string.Format(" and {0} = {1}",
						sqlStorageBase.GetValidFieldName(member.FieldAttribute.FieldName), parameterName));
				}

				// Add the parameter to the command.
				command.Parameters.Add(dataParameter);
			}
			command.CommandText = query.ToString();
			CompiledQuery compiledQuery = new CompiledQuery(command, QueryTypes.Delete);
			// Set the timeout for the query.
			compiledQuery.Timeout = objectExpression.Timeout;
			return compiledQuery;
		}

		#endregion

		#region BuildUpdateQuery

		/// <summary>
		/// Creates the <see cref="Opf3.Query.CompiledQuery">CompildQuery</see> that contains a storage dependent 
		/// update query by using the <see cref="Opf3.Query.PersistentObjectExpression">PersistentObjectExpression</see> given.
		/// </summary>
		/// <param name="poe">
		/// 	<see cref="Opf3.Query.PersistentObjectExpression">PersistentObjectExpression</see> that is converted
		/// to a <see cref="Opf3.Query.CompiledQuery">CompildQuery</see>.
		/// </param>
		/// <param name="sqlStorageBase">The storage that the query is compiled for.</param>
		/// <exception cref="ArgumentNullException">PersistentObjectExpression is null.</exception>
		/// <exception cref="ArgumentNullException">SqlStorageBase is null.</exception>
		protected virtual CompiledQuery BuildUpdateQuery(SqlStorageBase sqlStorageBase, PersistentObjectExpression poe)
		{
			if (sqlStorageBase == null)
				throw new ArgumentNullException("sqlStorageBase");
			if (poe == null)
				throw new ArgumentNullException("poe");
			// Check if the storage implements the ICustomDataTypesStorage interface.
			ICustomDataTypesStorage customDataTypesStorage = sqlStorageBase as ICustomDataTypesStorage;

			// Get the persistent, the mapping and the list of members.
			object persistent = poe.Persistent;
			IPartialPersist partial = persistent as IPartialPersist;
			TypeMapping mapping = TypeMapping.GetTypeMapping(persistent);
			IMemberInfoCollection members = mapping.Members;

			// Create the command.
			IDbCommand command = sqlStorageBase.CreateCommand();
			StringBuilder values = new StringBuilder();
			StringBuilder identifier = new StringBuilder();
			List<IDataParameter> identifierParameters = new List<IDataParameter>();

			for (int i = 0; i < members.Count; i++)
			{
				IMemberInfo mi = members[i];
				FieldAttribute fa = mi.FieldAttribute;

				// Skip if the member should not be updated.
				if ((fa.PersistBehaviours & PersistBehaviours.DontUpdate) != 0)
					continue;
				// Skip members are not to persist.
				if (!fa.Identifier && partial != null && !partial.IsMemberToPersist(mi.Name, PersistentOperations.Update))
					continue;

				object value = mi.GetValue(persistent);
				// Convert the values back to the storage compatible values.
				if (customDataTypesStorage != null)
					value = customDataTypesStorage.ChangeToDataType(value);

				// Get the database dependent parameter.
				string parameterName = null;
				IDataParameter dataParameter = CreateParameter(sqlStorageBase, i, value, mi.MemberType, out parameterName);

				if (fa.Identifier)
				{
					identifier.AppendFormat((identifier.Length == 0 ? "{0} = {1}" : " and {0} = {1}"), sqlStorageBase.GetValidFieldName(fa.FieldName), parameterName);
					// Add the parameter to the temporary list.
					identifierParameters.Add(dataParameter);
				}
				else
				{
					values.AppendFormat((values.Length == 0 ? "{0} = {1}" : ", {0} = {1}"), sqlStorageBase.GetValidFieldName(fa.FieldName), parameterName);
					// Add the parameter to the command object.
					command.Parameters.Add(dataParameter);
				}
			}

			// Copy the identifier parameters over.
			foreach (IDataParameter dp in identifierParameters)
			{
				command.Parameters.Add(dp);
			}

			command.CommandText = string.Format("update {0} set {1} where {2}", sqlStorageBase.GetValidEntityName(mapping.PersistentAttribute.Entity), values, identifier);

			CompiledQuery compiledQuery = new CompiledQuery(command, QueryTypes.Update);
			// Set the timeout for the query.
			compiledQuery.Timeout = poe.Timeout;
			return compiledQuery;
		}

		#endregion

		#region IStorageCommandBuilder Members

		/// <summary>
		/// Converts the <see cref="Opf3.Query.ObjectExpression">ObjectExpression</see> to
		/// a storage dependent <see cref="Opf3.Query.CompiledQuery">CompiledQuery</see>. This method should only be
		/// called by a class that implements the <see cref="IQuery">IQuery</see> interface.
		/// </summary>
		/// <param name="query"><see cref="Opf3.Query.IQuery">Query</see> object
		/// that is converted to a storage dependent <see cref="Opf3.Query.CompiledQuery">CompiledQuery</see>.
		/// </param>
		/// <param name="storage">The storage that the query is compiled for.</param>
		/// <returns>A storage dependent <see cref="Opf3.Query.CompiledQuery">CompiledQuery</see>.</returns>
		public virtual CompiledQuery ToCompiledQuery(IStorage storage, IQuery query)
		{
			if (storage == null)
				throw new ArgumentNullException("storage");
			if (query == null)
				throw new ArgumentNullException("query");
			// Check if the storage is inheriting from SQL storage base.
			SqlStorageBase ssb = storage as SqlStorageBase;
			if (ssb == null)
				throw new NotSupportedException("Only classes that inherit from SqlStorageBase are supported by this class.");

			ObjectExpression oe = null;
			if (query.QueryType == QueryTypes.Load)
			{
				oe = query as ObjectExpression;
				if (oe == null)
					throw new NotSupportedException("Only ObjectExpressions for load supported by this class.");
				// Build the select query by using the expression.
				return BuildSelectQuery(ssb, oe);
			}

			PersistentObjectExpression poe = query as PersistentObjectExpression;
			if (poe != null)
			{
				switch (poe.QueryType)
				{
					case QueryTypes.Update:
						return BuildUpdateQuery(ssb, poe);
					case QueryTypes.Delete:
						return BuildDeleteQuery(ssb, poe);
					case QueryTypes.Insert:
						return BuildInsertQuery(ssb, poe);
					default:
						throw new NotSupportedException("Only PersistentObjectExpression for update, delete or insert supported by this class.");
				}
			}
			else
			{
				oe = query as ObjectExpression;
				if (query == null)
					throw new NotSupportedException("Only ObjectExpression or PersistentObjectExpression supported by this class.");

				switch (oe.QueryType)
				{
					case QueryTypes.Insert:
						return BuildInsertQuery(ssb, oe);
					case QueryTypes.Delete:
						return BuildDeleteQuery(ssb, oe);
					default:
						throw new NotSupportedException("Only ObjectExpression for delete or insert supported by this class.");
				}
			}
		}

		/// <summary>
		/// Returns whether the <see cref="IStorageCommandBuilder">IStorageCommandBuilder</see> supports 
		/// the query type on the given storage.
		/// </summary>
		/// <param name="storage">The storage for which the query should be compiled.</param>
		/// <param name="query">The query that should be compiled to a storage dependent command.</param>
		/// <returns>True if the query can be parsed by the <see cref="IStorageCommandBuilder">IStorageCommandBuilder</see>.</returns>
		public virtual bool IsSupported(IStorage storage, IQuery query)
		{
			return (storage is SqlStorageBase && (query is ObjectExpression || query is PersistentObjectExpression));
		}

		#endregion

		/// <summary>
		/// Creates a parameter for the storage.
		/// </summary>
		/// <param name="storage">The storage class the parameter is created for.</param>
		/// <param name="index">The index of the parameter in the current query.</param>
		/// <param name="value">The value of the parameter.</param>
		/// <param name="type">The type of the parameter.</param>
		/// <param name="name">The name of the parameter. Returned by this routine.</param>
		/// <returns></returns>
		protected virtual IDataParameter CreateParameter(SqlStorageBase storage, int index, object value, Type type, out string name)
		{
			// Create the parameter name.
			name = "p" + index;
			
			IDataParameter p = storage.CreateParameter(index, ref name, value, type);
			return p;
		}

		/// <summary>
		/// Converts the command given to a command that is understood by the storage.
		/// </summary>
		/// <returns>Returns a string that represents the converted command.</returns>
		/// <remarks>
		/// This method returns a string representation of the command given. This string represents
		/// the name of the command that is exectued on the storage.
		/// <b>Attention</b>: The returned string must contain a tag "{0}" that is used by the parser
		/// to insert the name of the field on that the command is performed.
		/// </remarks>
		/// <param name="command">Command object that is converted to be understood by the storage.</param>
		protected virtual string ConvertCommand(OECommand command)
		{
			string result = string.Format("{0}({1}", command.Name, "{0}");
			foreach (string arg in command.Arguments)
			{
				result += string.Format(", {0}", arg);
			}

			return string.Format("{0})", result);
		}
	}
}
